--[[ scheme_type: generic author: Rulix modified_by: Alundaio --]] -- Set to true if you want punches to make actor drop active item local enable_drop_item = true -------------------- evid_facer = 18815 evid_steal_up_facer = 18816 actid_facer = 18815 actid_steal_up_facer = 18816 -------------------- local hit_sound = [[material\dead-body\collide\hithard06hl]] local facer_sets = {} local Enabled function on_game_start() local ini = ini_file("ai_tweaks\\xrs_facer.ltx") Enabled = ini:r_bool_ex("plugin","enable",false) if not (Enabled) then return end if (state_mgr.combat_action_ids) then state_mgr.combat_action_ids[actid_facer] = true state_mgr.combat_action_ids[actid_steal_up_facer] = true end RegisterScriptCallback("npc_on_hit_callback",hit_callback) facer_sets = { forbiddens = { factions = {} }, ranks = {}, check_prd = ini:r_float_ex("plugin","check_period") or 1500 } local ranks = utils_obj.get_rank_list() for k,v in ipairs(ranks) do if ini:section_exist(v) then facer_sets.ranks[v] = { power = (ini:r_float_ex(v,"power","float") or 50), k_mutant = (ini:r_float_ex(v,"k_mutant","float") or 1), k_stalker = (ini:r_float_ex(v,"k_stalker","float") or 1), k_actor = (ini:r_float_ex(v,"k_actor","float") or 1), k_friend = (ini:r_float_ex(v,"k_friend","float") or 0.3), min_delay = (ini:r_float_ex(v,"min_delay","float") or 1400), enable_vs_actor = (ini:r_bool_ex(v,"enable_vs_actor",true)), enable_vs_stalker = (ini:r_bool_ex(v,"enable_vs_stalker",true)), enable_vs_monster = (ini:r_bool_ex(v,"enable_vs_monster",true)) } end end facer_sets.ranks["default"] = { power = 50, k_mutant = 1, k_stalker = 1, k_actor = 1, k_friend = 0.1, min_delay = 1400, enable_vs_actor = false, enable_vs_stalker = false, enable_vs_monster = true } facer_sets.forbiddens.factions = parse_list(ini,"plugin","forbidden_factions",true) ini = nil end class "evaluator_facer" (property_evaluator) function evaluator_facer:__init(npc,name,storage) super (nil,name) self.st = storage self.st.check_time = time_global()+math.random(facer_sets.check_prd,5000) self.st.abtime = time_global() end function evaluator_facer:evaluate() --utils_data.debug_write("eva_facer") local npc,tg = self.object,time_global() if not (npc:alive()) then return false end if (IsWounded(npc)) then return false end if (npc:in_smart_cover()) then return false end if self.st.abtime > tg then return false end local target_id = load_var(npc,"facer_target",nil) local target = target_id and db.storage[target_id] and db.storage[target_id].object local be = npc:best_enemy() if not target then save_var(npc,"facer_target",nil) end if target and (not target:alive() and npc:animation_count() == 0) then save_var(npc,"facer_target",nil) target = nil end local spos = npc:position() if target then if (IsStalker(target) and npc:relation(target) >= game_object.enemy or IsMonster(target)) or (not be and target.health > 0.3) then local tpos = target:position() if tpos:distance_to_sqr(spos) < 3.8 and spos.y - tpos.y < 0.8 then return true end end end if be and self.st.check_time < tg then if not (check_enemy(be,npc)) then return false end if not (xr_combat_ignore.is_enemy(npc,be)) then save_var(npc,"facer_target",nil) return false end local epos = be:alive() and be:position() if epos:distance_to_sqr(spos) < 3.8 and spos.y - epos.y < 0.8 then save_var(npc,"facer_target",be:id()) self.st.check_time = tg+facer_sets.check_prd return true end end return false end function hit_callback(obj,amount,dir,who,bone_index) if not (Enabled) then return end if (bone_index == "from_death_callback") then return end if not(db.storage[obj:id()].facer) then return end if (who and IsStalker(who) and IsMonster(who) and who:alive() and check_enemy(who,obj)) then local target_id = load_var(obj,"facer_target",nil) local target = target_id and db.storage[target_id] and db.storage[target_id].object if (not target or obj:relation(target) ~= game_object.enemy or target:position():distance_to_sqr(obj:position()) > 4) then save_var(obj,"facer_target",who:id()) db.storage[obj:id()].facer.check_time = time_global()+facer_sets.check_prd end end end class "action_facer" (action_base) function action_facer:__init(npc,action_name,storage) super (nil,action_name) self.st = storage end function action_facer:initialize() action_base.initialize(self) local npc,wpn = self.object,self.object:active_item() self.end_time = time_global()+3000 --state_mgr.set_state(npc,"idle") npc:set_desired_position() npc:set_desired_direction() npc:set_movement_type(move.stand) npc:set_body_state(move.standing) if (wpn) then npc:set_item(object.aim1,wpn) end self.rank = ranks.get_obj_rank_name(npc) end function action_facer:execute() action_base.execute(self) --utils_data.debug_write("action_facer") local npc,wpn,tg = self.object,self.object:active_item(),time_global() local target_id = load_var(npc,"facer_target",nil) local target = target_id and db.storage[target_id] and db.storage[target_id].object if not (target) then self:cancel() return end if not (self.end_time) then self:cancel() return end npc:set_movement_type(move.stand) npc:set_sight(look.fire_point,utils_obj.safe_bone_pos(target,"bip01_neck")) if self.end_time < tg then --printf("facer[%s]:end_time < tg",npc:character_name()) self:cancel() elseif not self.hit_time then if npc:see(target) then --npc:position():distance_to_xz(target:position()) > wpn:position():distance_to_xz(target:position()) then local anim1,anim2 = get_anim(npc) npc:clear_animations() utils_obj.add_anim(npc,anim1) utils_obj.add_anim(npc,anim2) self.hit_time = tg+333 if npc:mental_state() == anim.free then self.hit_time = self.hit_time+222 end --printf("facer[%s]:punch %s %s",npc:character_name(),anim1,tostring(anim2)) else --printf("facer[%s]:not see target",npc:character_name()) end elseif self.hit_time < tg then --printf("facer[%s]:run hit",npc:character_name()) local spos,tpos = utils_obj.safe_bone_pos(npc,"bip01_neck"),utils_obj.safe_bone_pos(target,"bip01_neck") local ysub = spos.y - tpos.y local cls,bone = target:clsid() if cls == clsid.script_stalker or utils_obj.is_actor(nil,cls) then if ysub > 1.5 then elseif ysub > -0.3 then bone = "bip01_head" elseif ysub > -0.5 then bone = "bip01_spine2" elseif ysub > -0.8 then bone = "bip01_spine1" elseif ysub > -1.0 then bone = "bip01_pelvis" elseif ysub > -1.4 then bone = "bip01_l_calf" elseif ysub > -1.8 then bone = "bip01_l_foot" end elseif ysub < 1.2 and ysub > -1.8 then if (math.random(1,100)/100) > 0.4 then bone = "bip01_spine" else bone = "bip01_head" end end --printf("facer[%s]:ysub = %s, bone = %s",npc:character_name(),ysub,tostring(bone)) if bone and spos:distance_to_xz(tpos) < 3 and npc:animation_count() ~= 0 then local vals = facer_sets.ranks[self.rank] or facer_sets.ranks["default"] local power = vals.power > 0 and vals.power or 50 if cls == clsid.script_stalker then power = power*vals.k_stalker if npc:relation(target) ~= game_object.enemy then power = power*vals.k_friend end elseif IsMonster(nil,cls) then power = power*vals.k_mutant elseif utils_obj.is_actor(nil,cls) then power = power*vals.k_actor if npc:relation(target) ~= game_object.enemy then power = power*vals.k_friend end end local hi = hit() hi:bone(bone) hi.type = hit.strike hi.power = power/math.random(70,120) hi.impulse = math.random(50)*2 hi.draftsman = npc hi.direction = utils_obj.safe_bone_pos(target,bone):sub(utils_obj.safe_bone_pos(npc,"bip01_neck")) if true then --(math.random(1,100)/100) < hi.power*2 then if (enable_drop_item and utils_obj.is_actor(nil,cls)) then local item,slot = target:active_item(),target:active_slot() if item and (slot == 2 or slot == 3) then target:drop_item(item) end end end if (math.random(1,100)/100) < 0.33 then hi.type = hit.wound hi.power = hi.power+math.random(3)/10 hi.impulse = hi.impulse+math.random(100) --printf("facer[%s]:crit!",npc:character_name()) end if utils_obj.is_actor(nil,cls) then --utils_obj.get_sound([[affects\hit_fist]]):play_no_feedback(db.actor,sound_object.s2d,0,vector(),1.0) level.add_cam_effector("camera_effects\\fusker.anm",959,false,"") end utils_obj.get_sound(hit_sound):play_at_pos(target,target:position(),0) target:hit(hi) --printf("facer[%s]:hit to %s!!,power %s(%s), impulse %s",npc:character_name(),target:name(),hi.power,power,hi.impulse) self.hit_time = self.end_time else --printf("facer[%s]:fail! dxz = %s",npc:character_name(),spos:distance_to_xz(tpos)) self.hit_time = self.end_time end elseif npc:animation_count() == 0 then --printf("facer[%s]:end",npc:name()) self:cancel() end end function action_facer:cancel() --utils_data.debug_write("action_facer:cancel") local npc,tg = self.object,time_global() local vals = facer_sets.ranks[self.rank] or facer_sets.ranks["default"] local mind = vals.min_delay if mind < 1000 then mind = 1000 end self.st.check_time = tg+mind self.st.abtime = tg+vals.min_delay self.end_time = nil self.hit_time = nil save_var(npc,"facer_target",nil) if not IsWounded(npc) then -- or state_mgr.get_state(npc) == "idle" npc:clear_animations() end npc:set_sight(look.direction,npc:direction()) --printf("facer[%s]:cancel",npc:character_name()) end function action_facer:finalize() action_base.finalize(self) save_var(self.object,"facer_target",nil) --self:cancel() end class "evaluator_steal_up" (property_evaluator) function evaluator_steal_up:__init(npc,name,storage) super (nil,name) self.st = storage end function evaluator_steal_up:evaluate() --utils_data.debug_write("eva_steal_up") local npc,tg = self.object,time_global() if not (npc:alive()) then return false end if (IsWounded(npc)) then return false end local be = npc:best_enemy() if not (be) then return false end local target_id = load_var(npc,"facer_target",nil) local target = target_id and db.storage[target_id] and db.storage[target_id].object if not (target) then return false end if npc:active_item() then return false end local spos,tpos = npc:position(), target:position() local dist = tpos:distance_to_sqr(spos) local wpn = npc:best_weapon() if wpn or npc.health < 0.33 then return false elseif dist < 3.3 then return false elseif dist > 40 then save_var(npc,"facer_target",nil) return false end if not target then if npc:see(be) and not be:see(npc) then save_var(npc,"facer_target",be:id()) target = be end return false end local vid = target:level_vertex_id() if (vid < 4294967295 and npc:accessible(vid) and level.vertex_position(vid):distance_to_sqr(tpos) < 3) then return true end return false end class "action_steal_up" (action_base) function action_steal_up:__init(npc,action_name,storage) super (nil,action_name) self.st = storage end function action_steal_up:initialize() action_base.initialize(self) local npc,wpn = self.object,self.object:active_item() npc:set_desired_position() npc:set_desired_direction() npc:clear_animations() npc:set_movement_type(move.run) end function action_steal_up:execute() action_base.execute(self) local npc,tg = self.object,time_global() local target_id = load_var(npc,"facer_target",nil) local target = target_id and db.storage[target_id] and db.storage[target_id].object if not target then --printf("to_close[%s]:not target",npc:character_name()) return end --npc:set_dest_level_vertex_id(target:level_vertex_id()) if (npc:path_type() ~= game_object.level_path) then npc:set_path_type(game_object.level_path) end utils_obj.send_to_nearest_accessible_vertex(npc,target:level_vertex_id(),"xrs_facer") end function action_steal_up:finalize() action_base.finalize(self) end function check_enemy(enemy,obj) if not (enemy:alive()) then return false end if (IsWounded(enemy)) then return false end local cls = enemy:clsid() local rank = ranks.get_obj_rank_name(obj) local vals = facer_sets.ranks[rank] or facer_sets.ranks["default"] if (vals.enable_vs_actor and enemy:id() == AC_ID) then return true end if (vals.enable_vs_stalker and cls == clsid.script_stalker and obj:relation(enemy) > 0) then return true end if (vals.enable_vs_monster and IsMonster(nil,cls)) then return true end return false end function get_anim(npc) -- не буду связываться со ст манагер local wpn,slot,a1,a2 = npc:active_item(),0 if wpn and npc:weapon_unstrapped() then slot = wpn:animation_slot() end if npc:mental_state() == anim.free then a1 = "free_facer_"..slot if slot == 0 and (math.random(1,100)/100) < 0.5 then --a1 = "udar_0" -- TODO: find out where does this animation exists in CoC files end else a1 = "norm_facer_"..slot.."_0" a2 = "norm_facer_"..slot.."_1" end return a1,a2 end ----------------------------------------------------------------- ---------------------- binder ----------------------------------- function setup_generic_scheme(npc,ini,scheme,section,stype,temp) local st = xr_logic.assign_storage_and_bind(npc,ini,"facer",section,temp) end function add_to_binder(npc,ini,scheme,section,storage,temp) if not npc then return end local manager = npc:motivation_action_manager() if not manager then return end if (not npc:alive()) or (facer_sets.forbiddens.factions and facer_sets.forbiddens.factions[npc:character_community()]) or (facer_sets.ranks and not facer_sets.ranks[ranks.get_obj_rank_name(npc)]) then manager:add_evaluator(evid_facer,property_evaluator_const(false)) manager:add_evaluator(evid_steal_up_facer,property_evaluator_const(false)) --temp.needs_configured = false else manager:add_evaluator(evid_facer,evaluator_facer(npc,"eva_facer",storage)) manager:add_evaluator(evid_steal_up_facer,evaluator_steal_up(npc,"eva_steal_up_facer",storage)) end temp.action = {} temp.action[actid_facer] = action_facer(npc,"act_facer",storage) temp.action[actid_steal_up_facer] = action_steal_up(npc,"act_steal_up_facer",storage) if not temp.action[actid_facer] or not temp.action[actid_steal_up_facer] then return end temp.action[actid_facer]:add_precondition(world_property(evid_facer,true)) temp.action[actid_facer]:add_effect(world_property(evid_facer,false)) manager:add_action(actid_facer,temp.action[actid_facer]) temp.action[actid_steal_up_facer]:add_precondition(world_property(evid_facer,false)) temp.action[actid_steal_up_facer]:add_precondition(world_property(evid_steal_up_facer,true)) temp.action[actid_steal_up_facer]:add_effect(world_property(evid_steal_up_facer,false)) manager:add_action(actid_steal_up_facer,temp.action[actid_steal_up_facer]) end function configure_actions(npc,ini,scheme,section,stype,temp) if not npc then return end local manager = npc:motivation_action_manager() if not manager or not temp.action or not temp.action[actid_facer] or not temp.action[actid_steal_up_facer] then return end temp.action[actid_facer]:add_precondition(world_property(stalker_ids.property_alive,true)) temp.action[actid_facer]:add_precondition(world_property(xr_evaluators_id.sidor_wounded_base,false)) temp.action[actid_facer]:add_precondition(world_property(xr_evaluators_id.state_mgr+1,true)) temp.action[actid_steal_up_facer]:add_precondition(world_property(stalker_ids.property_alive,true)) temp.action[actid_steal_up_facer]:add_precondition(world_property(xr_evaluators_id.sidor_wounded_base,false)) temp.action[actid_steal_up_facer]:add_precondition(world_property(xr_evaluators_id.state_mgr+1,true)) local action local p = { xr_actions_id.alife, xr_actions_id.state_mgr + 2, stalker_ids.action_combat_planner, stalker_ids.action_danger_planner, xr_actions_id.stohe_meet_base+1 } --local p = {xr_actions_id.state_mgr + 1, , xr_actions_id.alife,xr_actions_id.stohe_meet_base+1} for i=1,#p do --printf("ACTION_ALIFE_ID(xrs_facer.configure_actions): " .. tostring(p[i])) action = manager:action(p[i]) if (action) then action:add_precondition(world_property(evid_facer,false)) action:add_precondition(world_property(evid_steal_up_facer,false)) end end end --[[ function disable_generic_scheme(npc,scheme,stype) local st = db.storage[npc:id()][scheme] if st then st.enabled = false end end --]] function npc_add_precondition(action) if not action then return end action:add_precondition(world_property(evid_facer,false)) end